#include <iostream>
#include <map>
#include <string>
#include <sstream>
#include <typeinfo>
#include <deque>


// ================================================================================

class Serializable {
  class AllocBase { 
  public:
    virtual Serializable* alloc() const = 0;
  };

  template<typename T>
  class Allocator : public AllocBase {
  public:
    Serializable* alloc() const {
      return new T;
    }
  };

public:
  template<typename T>
  Serializable(T* t) {
    if (_factories.find(typeid(T).name()) == _factories.end()) {
      _factories.insert(std::make_pair(typeid(T).name(), new Allocator<T>()));
    }
  }

  virtual ~Serializable() { }

  std::string serialize() const {
    std::stringstream s(std::ios_base::out | std::ios_base::binary);
    _serialize(s);
    return s.str();
  }

  static Serializable* deserialize(const std::string& s) {
    std::stringstream stream(s, std::ios_base::in | std::ios_base::binary);
    return _deserialize(stream);
  }

protected:  

  void _serialize(std::stringstream& s) const {
    const std::string& name = typeid(*this).name();
    size_t len = name.size();
    s << std::string((const char*)&len, sizeof(size_t));
    s << name;

    __serialize(s);
  }

  static Serializable* _deserialize(std::stringstream& s) {
    size_t len;
    char* name;

    s.read((char*)&len, sizeof(len));
    name = new char[len + 1];

    s.read(name, len);
    name[len] = 0;

    Serializable* obj = _factories[name]->alloc();
    obj->__deserialize(s);

    delete name;

    return obj;
  }

  virtual void __serialize(std::stringstream& s) const = 0;    

  virtual void __deserialize(std::stringstream& s) = 0;

private:
  static std::map<std::string, AllocBase*> _factories;   
};

std::map<std::string, Serializable::AllocBase*> Serializable::_factories;

// ================================================================================

class A : public Serializable {
public:
  A()
    : Serializable(this) { }

  A(int v) 
    : Serializable(this),
      _v(v) { }

  int value() const {
    return _v;
  }

protected:
  void __serialize(std::stringstream& s) const {
    s << _v;
  }

  void __deserialize(std::stringstream& s) {
    s >> _v;
  }

private:
  int _v;  
};


class B : public Serializable {
public:
  B()
    : Serializable(this) { }

  B(double v) 
    : Serializable(this),
      _v(v) { }

  double value() const {
    return _v;
  }

protected:
  void __serialize(std::stringstream& s) const {
    s << _v;
  }

  void __deserialize(std::stringstream& s) {
    s >> _v;
  }

private:
  double _v;  
};


class C : public Serializable {
public:
  C()
    : Serializable(this) { }

  C(int v) 
    : Serializable(this),
      _v(v) { }

  int value() const {
    return _v;
  }

  int show() const {
    std::cout << "The value encapsulated into the instance of the class C is " << _v << std::endl;
  }

protected:
  void __serialize(std::stringstream& s) const {
    s << _v;
  }

  void __deserialize(std::stringstream& s) {
    s >> _v;
  }

private:
  int _v;  
};

// --------------------------------------------------------------------------------

static void consume(const std::string& s) {
  Serializable* obj = Serializable::deserialize(s);

  if (typeid(*obj) == typeid(A)) {
    A* pa = (A*)obj;
    std::cout << "The value extracted from the object of the class A is: " << pa->value() << std::endl;
  } else if (typeid(*obj) == typeid(B)) {
    B* pb = (B*)obj;
    std::cout << "The value extracted from the object of the class B is: " << pb->value() << std::endl;
  } else if (typeid(*obj) == typeid(C)) {
    C* pc = (C*)obj;
    pc->show();
  }

  delete obj;
}


int main() {
  A a(5);
  B b(10.3);
  C c(2);

  consume(a.serialize());
  consume(b.serialize());
  consume(c.serialize());
}
